// Biblotecas estandar
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// Bibliotecas IPC
#include <sys/types.h>
#include <unistd.h>
// Clases que encapsulan los IPCs
#include "./IPC/Semaphore/Semaphore.h"
#include "./IPC/SharedMemory/ControlDataSharedMemory.h"
#include "./IPC/MessageQueue/DoorInputMessageQueue.h"
#include "./IPC/MessageQueue/DoorOutputMessageQueue.h"

/* Archivo que se encarga de inicializar todos los IPC a utilizar en los
 * procesos Puerta y llamar a los mismos */


/* Definición de variables estáticas para pasar argumentos a los procesos */
static char param1[16];
static char param2[16];
static int i;
static long idPersona = 0;
static char buffer[255];

/* Declaración de Funciones */
int procesarArgumentos(int argc, char * argv[], int *cantidadPersonas);
void init_IPCs(void);
void crearPuertas();
void crearPersonas(int cantidadPersonas);



int main(int argc, char* argv[])
{
	int cantidadPersonas = 0;
		
	if (procesarArgumentos(argc, argv, &cantidadPersonas)) {
		exit(1);
	}
	
	// Inicializo el entorno
	srand(getpid());
	init_IPCs();

	crearPuertas();
	crearPersonas(cantidadPersonas);
	return 0;
}

int procesarArgumentos(int argc, char * argv[], int *cantidadPersonas) {
	char buffer[100];

	if (argc != 2)
	{
		sprintf(buffer, "Error: Cantidad de argumentos pasados al programa incorrectos\n");
		strcat(buffer, "Uso: ./launcher [CANT_PERSONAS_A_ENTRAR]\n");
		write(fileno(stdout), buffer, strlen(buffer));
		return 1;
	}
	
	sscanf(argv[1], "%d", cantidadPersonas); 
	if (*cantidadPersonas < 1)
	{
		sprintf(buffer, "Error: La cantidad de puertas debe ser un número mayor o igual a 1");
		write(fileno(stdout), buffer, strlen(buffer));
		return 2;
	}
	
	return 0;
}



/* Esta función es aquella que debe inicializar a los IPC. En este caso se
 * precisan 4 IPCs:
 * semControl: Controla el acceso a la memoria compartida
 * EntryQueue: Colas por las cuales las personas piden entrar al Museo. 
 * Existe una cola de entrada por cada puerta.
 * ExitQueue: Idem EnterQueue, pero para la salida de personas del Museo.
 * shMem: Memoria Compartida donde se encontrarán las variables de control 
 */
void init_IPCs() {

	ControlData data;
	data.cantPersonas = 0;

	DoorInputMessageQueue entryDoorQueueInput = DoorInputMessageQueue();
	entryDoorQueueInput.create((char *) DIRECTORY, ID_Q_ENTRY_DOOR_INPUT);
	DoorOutputMessageQueue entryDoorQueueOutput = DoorOutputMessageQueue();
	entryDoorQueueOutput.create((char *) DIRECTORY, ID_Q_ENTRY_DOOR_OUTPUT);
	
	DoorInputMessageQueue exitDoorQueueInput = DoorInputMessageQueue();
	exitDoorQueueInput.create((char *) DIRECTORY, ID_Q_EXIT_DOOR_INPUT);
	DoorOutputMessageQueue exitDoorQueueOutput = DoorOutputMessageQueue();
	exitDoorQueueOutput.create((char *) DIRECTORY, ID_Q_EXIT_DOOR_OUTPUT);

	Semaphore semControl = Semaphore();	
	semControl.createSemaphore((char *) DIRECTORY, ID_SEMCONTROL, 1);
	semControl.initializeSemaphore(0, 1);
	
	Semaphore semDoor = Semaphore();
	semDoor.createSemaphore((char *) DIRECTORY, ID_SEMDOOR, CANT_DOORS);
	for (int i = 0; i < CANT_DOORS; i++) {
		semDoor.initializeSemaphore(i, 0);
	}

	// El tamaño de la memoria compartida es el de un único entero, que posee
	// como valor la cantidad de personas en el museo
	ControlDataSharedMemory shMem = ControlDataSharedMemory();
	shMem.createSharedMemory((char *) DIRECTORY, ID_SHMEM);
	shMem.write(&data);
	shMem.read(&data);

	sprintf(buffer, "Proceso Launcher - Valor inicial de cantPersonas: %d\n", 
	data.cantPersonas);
	write(fileno(stdout), buffer, strlen(buffer));
}

void crearPuertas() {
	pid_t pid;

	// Creo las 4 puertas de entrada
	for(i = 1; i <= CANT_DOORS; i++) {
		if ((pid = fork()) < 0) {
			sprintf(buffer, "Launcher Error: %s\n", strerror(errno));
			write(fileno(stdout), buffer, strlen(buffer));
		}				
		else if (pid == 0) {
			// Paso como parámetro a Puerta el número que identifica a 
			// la misma en el Museo (es un autoincrementable)
			sprintf(param1, "%d", i);
			execlp("./puertaEntrada", "puertaEntrada", param1, (char *) 0);
			sprintf(buffer, "Launcher Error: %s\n", strerror(errno));
			write(fileno(stdout), buffer, strlen(buffer));
		}
	}
	
	// Luego creo las cuatro puertas de salida
	for(i = 1; i <= CANT_DOORS; i++) {
		if ((pid = fork()) < 0) {
			sprintf(buffer, "Launcher Error: %s\n", strerror(errno));
			write(fileno(stdout), buffer, strlen(buffer));
		}				
		else if (pid == 0) {
			// Paso como parámetro a Puerta el número que identifica a 
			// la misma en el Museo (es un autoincrementable)
			sprintf(param1, "%d", i);
			execlp("./puertaSalida", "puertaSalida", param1, (char *) 0);
			sprintf(buffer, "Launcher Error: %s\n", strerror(errno));
			write(fileno(stdout), buffer, strlen(buffer));
		}
	}
}

void crearPersonas(int cantidadPersonas) {
	pid_t pid;

	while(cantidadPersonas != 0) {
		// Creo indefinidamente personas. El proceso Terminator 
		// será el encargado de terminar estos procesos
		idPersona++;
		if ((pid = fork()) < 0) {
			sprintf(buffer, "Launcher Error: %s\n", strerror(errno));
			write(fileno(stdout), buffer, strlen(buffer));
		}
		else if (pid == 0) {
			// Paso como parámetro a Persona el nombre del programa y la 
			// cantidad de puertas del museo
			sprintf(param1, "%d", CANT_DOORS); 
			sprintf(param2, "%ld", idPersona); 
			execlp("./persona", "persona", param1, param2, (char*) 0);
			sprintf(buffer, "Launcher Error: %s\n", strerror(errno));
			write(fileno(stdout), buffer, strlen(buffer));
		}
		else {
			// Espero un tiempo N y luego llamo a otro proceso Persona
			//printf(buffer, "Persona creada con el id N°%ld\n", idPersona);
			//write(fileno(stdout), buffer, strlen(buffer));
			usleep(500 * 1000);
			cantidadPersonas--;
		}
	} 
}
